///////////////////////////////////////////////////////////////////////////////
//    Copyright (c) 2021 CASTest Corporation Limited. All Rights Reserved    //
///////////////////////////////////////////////////////////////////////////////


#ifndef GATE_H
#define GATE_H

#include "Define.h"
class Fault;
// define gate primitive type
class Gate {

public:
    Gate() {};
    Gate(int id, int logic_type, string inst_name)
        : _id( id ),
          _logic_type( logic_type ),
          _inst_name( inst_name ),
          _dpi( -1 ),
          _dpo( -1 ),
          _c0( -1 ),
          _c1( -1 ),
          _ob( -1 ),
          _cop_c0( 0.0 ),
          _cop_c1( 0.0 ),
          _cop_ob( 0.0 ),
          _lamp_c0( 0 ),
          _lamp_c1( 0 ),
          _lamp_ob( 0 ),
          _value( LOGIC_X ),
          _is_global( false ),
          _special( NONE ),
          _is_cell_border( false ),
          _is_scheduled( false ),
          _is_stem( false ),
          _is_non_reconvergent( false ),
          _node_status( DEFAULT )
          {}

    ~Gate() {
        _fanins.clear();
        _fanouts.clear();
        _pred.clear();
    }

    // basic set/get
    void   SetName(string inst_name) { _inst_name = inst_name; }
    string GetName() const { return _inst_name; }

    void SetId(int id) { _id = id; }
    int  GetId() const { return _id; }

    void    SetValue(uint8_t value) { _value = value; }
    uint8_t GetValue() const { return _value; }

    // logic type id
    void SetLogicType(int logic_type) { _logic_type = logic_type; }
    int  GetLogicType() const { return _logic_type; }

    void SetDpi(int level) { _dpi = level; }
    int  GetDpi() const { return _dpi; }

    void SetDpo(int level) { _dpo = level; }
    int  GetDpo() const { return _dpo; }

    void SetC0(int c0) { _c0 = c0; }
    int  GetC0() const { return _c0; }

    void SetC1(int c1) { _c1 = c1; }
    int  GetC1() const { return _c1; }

    void SetOb(int ob) { _ob = ob; }
    int  GetOb() const { return _ob; }

    void   SetCopC0(double c0) { _cop_c0 = c0; }
    double GetCopC0() const { return _cop_c0; }

    void   SetCopC1(double c1) { _cop_c1 = c1; }
    double GetCopC1() const { return _cop_c1; }

    void   SetCopOb(double ob) { _cop_ob = ob; }
    double GetCopOb() const { return _cop_ob; }

    void   SetLampC0(int c0) { _lamp_c0 = c0; }
    int    GetLampC0() const { return _lamp_c0; }

    void   SetLampC1(int c1) { _lamp_c1 = c1; }
    int    GetLampC1() const { return _lamp_c1; }

    void   SetLampOb(int ob) { _lamp_ob = ob; }
    int    GetLampOb() const { return _lamp_ob; }

    void    SetGlobal(bool is_global_net) { _is_global = is_global_net; }
    bool    IsGlobal() const { return _is_global; }

    void AddToFanins(Gate* gate) { _fanins.push_back( gate ); }
    vector<Gate*>& GetAllFanins() { return _fanins; }
    Gate* GetFanin(int idx) { return _fanins[idx]; }

    void AddToFanouts(Gate* gate) { _fanouts.push_back( gate ); }
    vector<Gate*>& GetAllFanouts() { return _fanouts; }
    Gate* GetFanout(int idx) { return _fanouts[idx]; }

    int GetFaninsSize() const { return _fanins.size(); }
    int GetFanoutsSize() const {return _fanouts.size(); }

    void AddPredPin(int id) { _pred_pin_id.push_back(id); }
    vector<int>& GetPredPin() { return _pred_pin_id; }

    void AddSuccPin(int id) { _succ_pin_id.push_back(id); }
    vector<int>& GetSuccPin() { return _succ_pin_id; }

    void AddToPred(int idx) { _pred.push_back( idx ); }
    vector<int>& GetAllPred() { return _pred; }
    int  GetPred(int idx) const { return _pred[idx]; }
    void SetPred(int idx, int pred) { _pred[idx] = pred; }

    void ChangeFanin(int idx, Gate* gate) {
        _fanins[idx] = gate;
        _pred[idx] = gate->_id; 
    }
    void ChangeFanout(int idx, Gate* gate) {
        _fanouts[idx] = gate;
    }
    void ClearFanouts() {
        _fanouts.clear();
    }
    void ClearFanins() {
        _fanins.clear();
    }

    void SetSpecialGate(SpecialGate special) { _special = special;}
    SpecialGate GetSpecialGate() const { return _special; }

    void SetStem(bool is_stem) { _is_stem = is_stem; }
    bool IsStem() const { return _is_stem; }

    void SetCellBorder(bool is_cell_border) { _is_cell_border = is_cell_border; }
    bool IsCellBorder() const { return _is_cell_border; }

    void SetScheduled(bool is_scheduled) { _is_scheduled = is_scheduled; }
    bool IsScheduled() const { return _is_scheduled; }
    void SetNonReconvergent(bool is_non_reconvergent) { _is_non_reconvergent = is_non_reconvergent; }
    bool IsNonReconvergent() const { return _is_non_reconvergent; }


    bool IsFree() const  { return _ltype == LFREE; }
    bool IsHead() const  { return _ltype == HEAD;  }
    bool IsBound() const { return _ltype == BOUND; }
    void SetLtype(lineType type) { _ltype = type;  }
    lineType GetLtype() const { return _ltype; }

    void SetFfrId(uint64_t id) { _FFR_id = id;   }
    uint64_t GetFfrId() const  { return _FFR_id; }

    void AddFlist(Fault* fptr) { _flist.push_back(fptr); }
    vector<Fault*>& GetFlist() { return _flist; }

    uint8_t GetCtrInVal() const {
        switch(_logic_type){
            case G_AND:
            case G_NAND:
                return LOGIC_ZERO;
            case G_OR:
            case G_NOR:
                return LOGIC_ONE;
            default:
                break;
        }

        return LOGIC_X;
    }

    uint8_t GetCtrOutVal() const {
        switch(_logic_type){
            case G_AND:
            case G_NOR:
                return LOGIC_ZERO;
            case G_NAND:
            case G_OR:
                return LOGIC_ONE;
            default:
                break;
        }

        return LOGIC_X;
    }

    uint8_t GetNonCtrInVal() const {
        switch(_logic_type){
            case G_AND:
            case G_NAND:
                return LOGIC_ONE;
            case G_OR:
            case G_NOR:
                return LOGIC_ZERO;
            default:
                break;
        }

        return LOGIC_X;
    }

    uint8_t GetNonCtrOutVal() const {
        switch(_logic_type){
            case G_AND:
            case G_NOR:
                return LOGIC_ONE;
            case G_NAND:
            case G_OR:
                return LOGIC_ZERO;
            default:
                break;
        }

        return LOGIC_X;
    }

    // print gate information
    friend ostream& operator<<(ostream& os, Gate& gptr);
public:
    // basic gate info data
    // read-only variables.
    int     _id;                        // unique id (net id)
    int     _logic_type;                // logic type id
    string  _inst_name;                 // instance name
    int     _dpi;                       // logic depth from pi
    int     _dpo;                       // logic depth from po
    int     _c0;                        // controllability(scoap)
    int     _c1;                        // controllability(scoap)
    int     _ob;                        // observability(scoap)
    double  _cop_c0;                    // controllability(cop)
    double  _cop_c1;                    // controllability(cop)
    double  _cop_ob;                    // observability(cop)
    int     _lamp_c0;
    int     _lamp_c1;
    int     _lamp_ob;
    uint8_t _value;                     // value for atpg (0,1,LOGIC_X,D,DB)
    bool    _is_global;                 // clk, set, reset, no need to add stems
    vector<Gate*>   _fanins;            // fanin gates of current gate
    vector<Gate*>   _fanouts;           // fanout gates of current gate
    vector<int>     _pred_pin_id;       //
    vector<int>     _succ_pin_id;       //
    vector<int>     _pred;              // fanin gate's id read from vy file, used to find fanins to build connection
    SpecialGate     _special;           // used to identify some special gate such as {test_si, test_se, clk, set, reset}
    bool _is_cell_border;               // judge a gate whether is a cell input or cell output
    bool _is_scheduled;                 // eventlist
    bool _is_stem;                      // stem gate flag
    bool _is_non_reconvergent;          // use to compute dominators
    int      _mark_id;                  // general using mark.
    lineType _ltype;                    // line type defined by FAN
    uint64_t _FFR_id;
    int      freach1;

    //writable variables.
    vector<uint8_t>  _atpg_val;
    vector<uint64_t> _fault_free_val;
    vector<uint64_t> _faulty_val;
    // faults at gate
    vector<Fault*>  _flist;

    // stem region extraction.
    int _line_status;
    SRstatus _node_status;
    int _fob_counter;
};

inline ostream& operator<<(ostream& os, Gate& gptr) {
    os << " Gate  id: "   << gptr._id;
    os << " inst name: "  << gptr._inst_name;
    os << " logic id: "   << gptr._logic_type;
    os << " fanin size: " << gptr.GetFaninsSize() << " fanin gates id: ";
    for (auto& elem : gptr._pred) {
        os << elem << " ";
    }
    os << " is cell border: " << boolalpha << gptr._is_cell_border;
    os << " is scheduled: "   << boolalpha << gptr._is_scheduled;
    os << " is stem: "        << boolalpha << gptr._is_stem;

    return os;
}

#endif // GATE_H